﻿using WPFTrees.Parser;
using WPFTrees.DataStructures;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Data;
using WPFTrees.Layouts;
using System.Diagnostics;
using System;
using WPFTrees.Tools;
namespace WPFTrees
{
    public partial class MainWindow
    {
        public void RenderTree()
        {
            RenderTree(this.tree);
        }

        public  void RenderTree(NTree<NewickNode> tree)
        {           
            layout.ExecuteLayout(canvas,tree);            
        }        

        void BuildTree(NTree<NewickNode> tree)
        {
            labels.Clear();
            Size size = canvas.RenderSize;
            Point rootPos = new Point(canvas.RenderSize.Width / 2.0, 0);
            BuildVisualTreeRecursive(tree.GetRoot());         
        }

        void BuildVisualTreeRecursive(NTreeNode<NewickNode> root)
        {
            //draw root                              
            FrameworkElement visualElement = CreateNodeElement(root);
            canvas.Children.Add(visualElement);

            // draw children

            foreach (NTreeNode<NewickNode> child in root.Children)
            {
                if (child != null)
                {
                    NewickNode node = child.Value();

                    node.BranchToParentElement = layout.BranchDrawer.CreateBranch();                   
                    canvas.Children.Add(node.BranchToParentElement);

                    if (node.GroupInfo != null)
                    {
                        Shape groupShape = (layout as ISupportGroups).CreateGroupShape(node);
                        groupShape.Tag = node;
                        node.GroupInfo.Shape = groupShape;

                        groupShape.MouseEnter += new MouseEventHandler(groupShape_MouseEnter);
                        groupShape.MouseLeave += new MouseEventHandler(groupShape_MouseLeave);
                        groupShape.MouseMove += new MouseEventHandler(groupShape_MouseMove);
                        canvas.Children.Add(node.GroupInfo.Shape);
                        Canvas.SetZIndex(node.GroupInfo.Shape, -10);
                    }
                    BuildVisualTreeRecursive(child);
                }
            }
        }

        void groupShape_MouseMove(object sender, MouseEventArgs e)
        {
            Shape groupShape = sender as Shape;
            NewickNode node = groupShape.Tag as NewickNode;

            statusBar1.Items[0] = node.GroupInfo.Name;
        }

        void groupShape_MouseLeave(object sender, MouseEventArgs e)
        {
            Shape groupShape = sender as Shape;
            groupShape.StrokeThickness = 0.0;
            groupShape.Stroke = null;
        }

        void groupShape_MouseEnter(object sender, MouseEventArgs e)
        {
            Shape groupShape = sender as Shape;
            groupShape.StrokeThickness = 2.0;
            groupShape.Stroke = Brushes.Blue;
        }

        #region Nodes
        Shape CreateLeafNodeShape(Canvas nodeCanvas, NewickNode node)
        {
            Ellipse nodeShape = new Ellipse();
            nodeShape.StrokeThickness = 2;
            nodeShape.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
            //nodeShape.Width = nodeShape.Height = nodeCanvas.Width = nodeCanvas.Height = DefaultSettings.Default.LeafNodeHeight;
            Binding widthBinding = new Binding("LeafNodeHeight");
            widthBinding.Source = DefaultSettings.Default;
            nodeShape.SetBinding(Shape.WidthProperty, widthBinding);
            //TODO: InnerNodeWidth settings
            nodeShape.SetBinding(Shape.HeightProperty, widthBinding);
            nodeCanvas.SetBinding(Canvas.WidthProperty, widthBinding);
            nodeCanvas.SetBinding(Canvas.HeightProperty, widthBinding);
            nodeCanvas.Children.Add(nodeShape);
            Label label = CreateLabel(node.Label);
            label.Padding = new Thickness(0);
            label.Tag = node;
            Binding b = new Binding("LeafLabelColor");
            b.Source = DefaultSettings.Default;
            label.SetBinding(Label.ForegroundProperty, b);
            nodeCanvas.Children.Add(label);
            nodeCanvas.Tag = label;
            Canvas.SetLeft(label, DefaultSettings.Default.LeafNodeHeight / 2);
            Canvas.SetTop(label, DefaultSettings.Default.LeafNodeHeight / 2);
            Binding b2 = new Binding("LeafColor");
            b2.Source = DefaultSettings.Default;
            nodeShape.SetBinding(Ellipse.StrokeProperty, b2);
            return nodeShape;
        }

        Shape CreateInnerNodeShape(Canvas nodeCanvas, NewickNode node)
        {
            Ellipse nodeShape = new Ellipse();
            nodeShape.StrokeThickness = 2;
            nodeShape.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
            //nodeShape.Width = nodeShape.Height = nodeCanvas.Width = nodeCanvas.Height = DefaultSettings.Default.InnerNodeHeight;

            Binding widthBinding = new Binding("InnerNodeHeight");
            widthBinding.Source = DefaultSettings.Default;
            nodeShape.SetBinding(Shape.WidthProperty, widthBinding);
            //TODO: InnerNodeWidth settings
            nodeShape.SetBinding(Shape.HeightProperty, widthBinding);
            nodeCanvas.SetBinding(Canvas.WidthProperty, widthBinding);
            nodeCanvas.SetBinding(Canvas.HeightProperty, widthBinding);
            nodeCanvas.Children.Add(nodeShape);
            Label label = CreateLabel(node.Label);
            label.Padding = new Thickness(0);
            Binding b = new Binding("LeafLabelColor");
            b.Source = DefaultSettings.Default;
            label.SetBinding(Label.ForegroundProperty, b);
            nodeCanvas.Children.Add(label);
            Canvas.SetLeft(label, DefaultSettings.Default.InnerNodeHeight / 2);
            Canvas.SetTop(label, DefaultSettings.Default.InnerNodeHeight / 2);
            Binding b2 = new Binding("LeafColor");
            b2.Source = DefaultSettings.Default;
            nodeShape.SetBinding(Ellipse.StrokeProperty, b2);
            
            nodeShape.Cursor = Cursors.Cross;

            // draw smaller circle for inner node
            if (DefaultSettings.Default.DrawInnerNodes)
            {
                nodeShape.Stroke = Brushes.Red; //TODO : settings
            }
            else
            {
                nodeShape.Stroke = Brushes.Transparent;
            }
            return nodeShape;
        }
        /// <summary>
        /// Creates visual representation of tree node
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        FrameworkElement CreateNodeElement(NTreeNode<NewickNode> node)
        {
            Shape nodeElement;

            Canvas nodeCanvas = new Canvas();
            NewickNode newickNode = node.Value();
            if (newickNode.IsLeaf)
            {
                nodeElement = CreateLeafNodeShape(nodeCanvas, newickNode);


            }
            else
            {
                // inner node
                nodeElement = CreateInnerNodeShape(nodeCanvas, newickNode);


                nodeElement.MouseEnter += new MouseEventHandler(nodeElement_MouseEnter);
                nodeElement.MouseLeave += new MouseEventHandler(nodeElement_MouseLeave);
                nodeElement.MouseDown += new MouseButtonEventHandler(nodeElement_MouseDown);

                nodeElement.ContextMenu = CreateContextMenuForNode(node);
            }

            newickNode.VisualElement = nodeCanvas;
            nodeElement.Tag = node;
            nodeElement.ToolTip = newickNode.Label;
            return nodeCanvas;
        }

        /// <summary>
        /// Vytvori kontextovou nabidku pro uzel
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        ContextMenu CreateContextMenuForNode(NTreeNode<NewickNode> node)
        {
            #region create context menu
            ContextMenu contextMenu = new ContextMenu();
            contextMenu.Tag = node;
            MenuItem mi = new MenuItem();
            mi.Header = "Collapse subtree";
            mi.Click += new RoutedEventHandler(miInnerNodeCollapse_Click);
            mi.Tag = node;
            contextMenu.Items.Add(mi);
            mi = new MenuItem();
            mi.Header = "Expand subtree";
            mi.Click += new RoutedEventHandler(miInnerNodeExpand_Click);
            mi.Tag = node;
            contextMenu.Items.Add(mi);

            CreateGroupContextMenuItems(node, contextMenu);

            mi = new MenuItem();
            mi.Header = "Rotate right";
            mi.Click += new RoutedEventHandler(miRotateChildrenRight_Click);
            mi.Tag = node;
            contextMenu.Items.Add(mi);

            mi = new MenuItem();
            mi.Header = "Rotate left";
            mi.Click += new RoutedEventHandler(miRotateChildrenLeft_Click);
            mi.Tag = node;
            contextMenu.Items.Add(mi);

            mi = new MenuItem();
            mi.Header = "Ladderize right";
            mi.Click += new RoutedEventHandler(miLadderizeRight_Click);
            mi.Tag = node;
            contextMenu.Items.Add(mi);

            mi = new MenuItem();
            mi.Header = "Ladderize left";
            mi.Click += new RoutedEventHandler(miLadderizeLeft_Click);
            mi.Tag = node;
            contextMenu.Items.Add(mi);

            contextMenu.Opened += new RoutedEventHandler(contextMenu_Opened);

            return contextMenu;

            #endregion
        }

        /// <summary>
        /// handler udalosti pri otevreni kontextove nabidky,
        /// podle stavu uzlu nastavi viditelnost nabidek skupin, atd.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void contextMenu_Opened(object sender, RoutedEventArgs e)
        {
            ContextMenu menu = sender as ContextMenu;
            NTreeNode<NewickNode> node = menu.Tag as NTreeNode<NewickNode>;

            //nastavi viditelnost
            MenuItem miCollapse = menu.Items[0] as MenuItem;
            MenuItem miExpand = menu.Items[1] as MenuItem;
            if (node.CountChildren == 0)
            {
                miExpand.Visibility = Visibility.Collapsed;
                miCollapse.Visibility = Visibility.Collapsed;
            }
            else
            {
                bool bCollapsed = node.Children[0].Value().IsCollapsed;
                miExpand.Visibility = bCollapsed ? Visibility.Visible : Visibility.Collapsed;
                miCollapse.Visibility = !bCollapsed ? Visibility.Visible : Visibility.Collapsed;
            }

            MenuItem miDefineGroup = menu.Items[2] as MenuItem;
            MenuItem miGroupProperties = menu.Items[3] as MenuItem;
            MenuItem miDeleteGroup = menu.Items[4] as MenuItem;
            bool bHasGroup = (node.Value().GroupInfo != null);
            miDefineGroup.Visibility = !bHasGroup ? Visibility.Visible : Visibility.Collapsed;
            miDeleteGroup.Visibility = miGroupProperties.Visibility = bHasGroup ? Visibility.Visible : Visibility.Collapsed;

        }      

        void nodeElement_MouseDown(object sender, MouseButtonEventArgs e)
        {
            tree.SelectedNode = ((sender as FrameworkElement).Tag as NTreeNode<NewickNode>);
        }

        void miRotateChildrenRight_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = sender as MenuItem;
            NTreeNode<NewickNode> node = mi.Tag as NTreeNode<NewickNode>;
            Tools.TreeTools.RotateRight(node);
            RenderTree(CurrentTree);
        }

        void miRotateChildrenLeft_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = sender as MenuItem;
            NTreeNode<NewickNode> node = mi.Tag as NTreeNode<NewickNode>;
            Tools.TreeTools.RotateLeft(node);
            RenderTree(CurrentTree);
        }

        void miLadderizeLeft_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = sender as MenuItem;
            NTreeNode<NewickNode> node = mi.Tag as NTreeNode<NewickNode>;
            Tools.TreeTools.LadderizeLeft(node);
            RenderTree(CurrentTree);
        }

        void miLadderizeRight_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = sender as MenuItem;
            NTreeNode<NewickNode> node = mi.Tag as NTreeNode<NewickNode>;
            Tools.TreeTools.LadderizeRight(node);
            RenderTree(CurrentTree);
        }

        void nodeElement_MouseEnter(object sender, MouseEventArgs e)
        {
            NTreeNode<NewickNode> node = (sender as Shape).Tag as NTreeNode<NewickNode>;
            if (node.CountImmediateChildren > 0)
            {
                statusBar1.Items[0] = "Right click to select action on this subtree.";
            }
            // zvyrazni podstrom
            TreeTools.ColorSubtree(node, Brushes.Green);
        }

        void nodeElement_MouseLeave(object sender, MouseEventArgs e)
        {
            NTreeNode<NewickNode> node = (sender as Shape).Tag as NTreeNode<NewickNode>;
            statusBar1.Items[0] = "";
            TreeTools.ColorSubtree(node, Brushes.Black);
        }

        void miInnerNodeCollapse_Click(object sender, RoutedEventArgs e)
        {
            NTreeNode<NewickNode> node = (((MenuItem)sender).Tag) as NTreeNode<NewickNode>;
            Tools.TreeTools.CollapseOrExpandSubtree(node, true);
            RenderTree(CurrentTree);
        }

        void miInnerNodeExpand_Click(object sender, RoutedEventArgs e)
        {
            NTreeNode<NewickNode> node = (((MenuItem)sender).Tag) as NTreeNode<NewickNode>;
            Tools.TreeTools.CollapseOrExpandSubtree(node, false);
            RenderTree(CurrentTree);
        }
        #endregion

        #region Labels

        bool rotateLabel;
        Label rotatedLabel;

        /// <summary>
        /// Vytvori vizualni objekt popisku uzlu.
        /// </summary>
        /// <param name="text">Text popisku.</param>
        /// <returns>Label vizualne reprezentujici popisek uzlu.</returns>
        Label CreateLabel(string text)
        {
            Label label = new Label();
            label.Name = "label";
            label.Content = text;
            label.FontFamily = DefaultSettings.Default.LabelFontFamily;
            label.FontSize = DefaultSettings.Default.LabelFontSize;
            //label.Background = Brushes.Red;
            label.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
            label.MouseEnter += new MouseEventHandler(label_MouseEnter);
            label.MouseLeave += new MouseEventHandler(label_MouseLeave);
            label.MouseLeftButtonUp += new MouseButtonEventHandler(label_MouseLeftButtonUp);
            label.MouseLeftButtonDown += new MouseButtonEventHandler(label_MouseLeftButtonDown);
            label.MouseDown += new MouseButtonEventHandler(label_MouseDown);
            label.MouseMove += new MouseEventHandler(label_MouseMove);
            label.BorderThickness = new Thickness(1.0);

            // prida label do seznamu labelu
            labels.Add(label);

            Binding b = new Binding("LabelFontFamily");
            b.Source = DefaultSettings.Default;
            label.SetBinding(Label.FontFamilyProperty, b);

            Binding b1 = new Binding("LabelFontSize");
            b1.Source = DefaultSettings.Default;
            label.SetBinding(Label.FontSizeProperty, b1);

            label.ContextMenu = CreateLabelContextMenu(label);
            return label;
        }

        void label_MouseDown(object sender, MouseButtonEventArgs e)
        {
            Label label = (sender as Label);
            if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
            {
                // otacim popiskem
                rotateLabel = true;
                rotatedLabel = label;
                label.CaptureMouse();
                //TODO: rotate;
                //label.LayoutTransform.
                label.Foreground = Brushes.Aqua;               

            }
        }

        void label_MouseMove(object sender, MouseEventArgs e)
        {
            Label label = (sender as Label);
             NewickNode node = label.Tag as NewickNode;
             if (node != null) // je to leaf
             {
                 statusBar1.Items[0] = "OTU:" + node.Label + "."
                     + "Left click to rotate label. Hold alt to move label."
                     + "Right click to bring up property dialog.";
             }
           
        }

        //
        // Vytvori kontextove menu popisku a navaze ho na popisek
        // 
        ContextMenu CreateLabelContextMenu(Label label)
        {
            ContextMenu contextMenu = new ContextMenu();
            contextMenu.Tag = label;
            MenuItem mi = new MenuItem();
            mi.Header = "Properties...";
            mi.Click += new RoutedEventHandler(miLabelShowProperties_Click);
            mi.Tag = label;
            contextMenu.Items.Add(mi);
            mi = new MenuItem();
            mi.Header = "Hide";
            mi.Click += new RoutedEventHandler(miLabelHide_Click);
            mi.Tag = label;
            contextMenu.Items.Add(mi);
            
            contextMenu.Opened += new RoutedEventHandler(contextMenuLabel_Opened);

            return contextMenu;
        }


        void miLabelShowProperties_Click(object sender, RoutedEventArgs e)
        {
            PropertyDialogs.LabelPropertyDlg dlg = new PropertyDialogs.LabelPropertyDlg();
            if (dlg.ShowDialog() == true)
            { 
                // TODO: nastavit vlastnosti z dialogu
            }
        }

        void miLabelHide_Click(object sender, RoutedEventArgs e)
        {
        }


        void contextMenuLabel_Opened(object sender, RoutedEventArgs e)
        {
            ContextMenu menu = sender as ContextMenu;
            Label label = menu.Tag as Label;

            // TODO: nastavit menu podle stavu labelu

        }      
        #endregion       

        #region Groups
        void CreateGroupContextMenuItems(NTreeNode<NewickNode> node, ContextMenu contextMenu)
        {
            MenuItem mi = new MenuItem();
            mi.Header = "Define group...";
            mi.Click += new RoutedEventHandler(miInnerNodeDefineGroup_Click);
            mi.Tag = node;
            contextMenu.Items.Add(mi);
            mi = new MenuItem();
            mi.Header = "Group properties...";
            mi.Tag = node;
            mi.Click += new RoutedEventHandler(miInnerNodeShowGroupProperties_Click);
            contextMenu.Items.Add(mi);

            mi = new MenuItem();
            mi.Header = "Delete group";
            mi.Tag = node;
            mi.Click += new RoutedEventHandler(miDeleteGroup_Click);
            contextMenu.Items.Add(mi);

        }

        void miDeleteGroup_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = sender as MenuItem;
            NTreeNode<NewickNode> node = mi.Tag as NTreeNode<NewickNode>;
            GroupInfo info = node.Value().GroupInfo;
            if (info != null)
            {
                canvas.Children.Remove(info.Shape);
                node.Value().GroupInfo = null;
            }
        }

        void miInnerNodeShowGroupProperties_Click(object sender, RoutedEventArgs e)
        {
            MenuItem mi = sender as MenuItem;
            NTreeNode<NewickNode> treeNode = mi.Tag as NTreeNode<NewickNode>;
            GroupDlg dlg = new GroupDlg(treeNode.Value().GroupInfo);
            if (dlg.ShowDialog() == true)
            {
                GroupInfo info = treeNode.Value().GroupInfo;
                info.Name = dlg.GroupName;
                info.Color = dlg.GroupColor;
                (treeNode.Value().GroupInfo.Shape as Shape).ToolTip = info.Name;
            }
        }

        void miInnerNodeDefineGroup_Click(object sender, RoutedEventArgs e)
        {
            // predpoklada se, ze layout podporuje skupiny (jinak by toto 
            // nastaveni nebylo uzivateli pristupne)
            GroupDlg dlg = new GroupDlg();
            if (dlg.ShowDialog() == true)
            {
                MenuItem mi = sender as MenuItem;
                NTreeNode<NewickNode> treeNode = mi.Tag as NTreeNode<NewickNode>;
                NewickNode node = treeNode.Value();
                node.GroupInfo = new GroupInfo();
                node.GroupInfo.Color = dlg.GroupColor;
                node.GroupInfo.Name = dlg.GroupName;
                Shape shape = (layout as ISupportGroups).CreateGroupShape(node);
                canvas.Children.Add(shape);
                Canvas.SetZIndex(shape, -10);
                node.GroupInfo.Shape = shape;

                (node.GroupInfo.Shape as Shape).ToolTip = node.GroupInfo.Name;

                RenderTree(CurrentTree);
            }
        }


        #endregion                     

       
       
    }
}